﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using Logic;
using System.Web.Script.Serialization;
using System.Web.SessionState;
using oAuth;
using System.Text.RegularExpressions;
using System.Configuration;


namespace BLL.Components
{
    public class AuthorizeModule : IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.BeginRequest += new EventHandler(OnBeginRequest);
            application.PreRequestHandlerExecute += new EventHandler(context_BeginRequest);
        }

        public void Dispose()
        {
        }

        private void OnBeginRequest(object sender, EventArgs e)
        {
            var httpApplication = (HttpApplication)sender;
            SetUpSessionForFlash(httpApplication);
        }
        void context_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication Application = sender as HttpApplication;
           
            Log.Write("**********************************************************");
            Log.Write("Method:" + Application.Request.HttpMethod);
            Log.Write("context_BeginRequest " + Application.Request.Form.ToString() + Application.Request.QueryString.ToString());

            string urlPath = Application.Request.Url.AbsolutePath.ToLower();
            Log.Write("path: " + urlPath);

            //// no favicon
            //if (urlPath.EndsWith(".ico"))
            //    EndResponse(Application.Response, new Object());

            var logout = AccessTokenLogin(Application);

            if (IsPermitedPage(urlPath))
                return;
            
            if (Logic.Constants.IsGmail)
                AuthorizeGoogle(Application, urlPath);
            else
                AuthorizeConduit(Application, logout, urlPath);
        }

        private void AuthorizeConduit(HttpApplication Application, bool logout, string urlPath)
        {
            Log.Write("AuthorizeConduit");
            
            if (!CheckCsrfToken(new HttpContextWrapper(Application.Context)) || !BLL.Users.EnsureSession())
            {
                Log.Write("no session");

                if (!StopPreLoginRequests(Application))
                {
                    if (Application.Request.Headers["Accept"].Contains("application/json"))
                        EndResponse(Application.Response, new GadgetResponse<Object> { ErrorMessage = "Not logged in", Status = GadgetResponseStatus.Not_Logged_In, RedirectUrl = Application.Request.UrlReferrer.ToString().ToLower().Replace ("http://","https://") });
                    else
                        HttpContext.Current.Response.Redirect(BLL.Users.GetAuthorizationLink(Application.Request.Url.ToString().ToLower().Replace("http://", "https://"), logout));
                }
            }
            else if (urlPath.EndsWith("/login/keepAlive", StringComparison.OrdinalIgnoreCase))// this call is performed in order to refresh the session
                RefreshSession(Application);
        }

        private void RefreshSession(HttpApplication Application)
        {
            if (!new QueryComponent().RefershCall())
            {// session canot be refreshed oauth has to be performed again
                Log.Write("EnsureSession failed");
                oAuthClarizen oAuth = oAuthClarizen.Get();
                oAuth.Identifier = BLL.Users.GetGoogleID();
                var authorizeLink = oAuth.AuthorizationLinkGet();
                EndResponse(Application.Response, new GadgetResponse<Object> { ErrorMessage = "Fail - unable to connect", Status = GadgetResponseStatus.Oauth_Connection_Error, RedirectUrl = authorizeLink });
            }
            else // session has been refrehed 
                EndResponse(Application.Response, new GadgetResponse<Object> { Status = GadgetResponseStatus.Success });
        }

        private static bool IsPermitedPage(string urlPath)
        {
            var prefix = Constants.IsGmailCanvas ? "/gmail/canvas" : Constants.IsGmail ? "/gmail" : "";

            return urlPath.EndsWith(".css") ||
                            urlPath.EndsWith(".ico") ||
                            urlPath.StartsWith(prefix + "/uploadify") ||
                            urlPath.StartsWith(prefix + "/images") ||
                            urlPath.StartsWith(prefix + "/conduit") ||
                            urlPath.StartsWith(prefix + "/css/") ||
                            urlPath.StartsWith(prefix + "/js/") ||
                //urlPath.StartsWith(prefix + "/test") ||
                            urlPath.StartsWith(prefix + "/content") ||
                            urlPath.StartsWith(prefix + "/errors/") ||
                            urlPath.StartsWith(prefix + "/scripts") ||
                            urlPath.StartsWith(prefix + "/oauth") ||
                            urlPath.StartsWith(prefix + "/gadgets") ||
                            urlPath.StartsWith(prefix + "/clarizen/create") ||
                            urlPath.StartsWith(prefix + "/clarizen/adminactivate") ||
                            urlPath.StartsWith(prefix + "/tinymce") ||
                            urlPath.EndsWith(".xml") ||
                            urlPath.StartsWith(prefix + "/login/login") ||
                            urlPath.StartsWith(prefix + "/login/tokenlogin") ||
                            urlPath.StartsWith(prefix + "/login/logout") ||
                            urlPath.StartsWith(prefix + "/test/test");
        }

        private static bool AccessTokenLogin(HttpApplication Application)
        {
            // set to true if we need to delete the access token on toolbar
            Boolean logout = false;

            if (!string.IsNullOrEmpty(Application.Request["accessToken"]))
            {
                try
                {
                    string id;
                    try
                    {
                       id = BLL.Encryption.Decrypt(Application.Request["accessToken"]);
                    }
                    catch
                    {
                       id = BLL.Encryption.Decrypt(HttpUtility.UrlDecode ( Application.Request["accessToken"]));
                    }
                    Log.Write("id:" + id);
                    BO.User user = BLL.Users.GetByID(id);

                    logout = !Users.EnsureSession(user, true);
                }
                catch
                {
                    logout = true;
                }
                
                if(!logout)
                {
                    Log.Write("logged in with accessToken");
                    Log.Write("session=" + Users.GetClarizenSession());
                }
            }
            return logout;
        }

        /// <summary>
        /// work around flash not sending cookies
        /// </summary>
        /// <param name="httpApplication"></param>
        private void SetUpSessionForFlash(HttpApplication httpApplication)
        {
            /* we guess at this point session is not already retrieved by application so we recreate cookie with the session id... */
            try
            {
                string session_param_name = "ASPSESSID";
                string session_cookie_name = "ASP.NET_SessionId";
                if (httpApplication.Request.Form[session_param_name] != null)
                {
                    UpdateCookie(httpApplication, session_cookie_name, httpApplication.Request.Form[session_param_name]);
                }
                else if (httpApplication.Request.QueryString[session_param_name] != null)
                {
                    UpdateCookie(httpApplication, session_cookie_name, httpApplication.Request.QueryString[session_param_name]);
                }
            }
            catch
            {
            }
        }

        private void UpdateCookie(HttpApplication application, string cookie_name, string cookie_value)
        {
            var httpApplication = (HttpApplication)application;

            HttpCookie cookie = httpApplication.Request.Cookies.Get(cookie_name);
            if (null == cookie)
            {
                cookie = new HttpCookie(cookie_name);
            }
            cookie.Value = cookie_value;
            httpApplication.Request.Cookies.Set(cookie);
        }

        private Boolean CheckCsrfToken(HttpContextBase context)
        {
            try
            {
                if (context.Request.HttpMethod.ToLower() == "get" || context.Request.Url.AbsolutePath.ToLower().StartsWith("/documents/create"))
                    return true;
                try
                {
                    if (ConfigurationManager.AppSettings["DisableCSRF"].ToString().ToLower() == "true")
                        return true;
                }
                catch { }
                System.Web.Helpers.AntiForgery.Validate(context, null);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// hack to prevent calls from toolbar before login was performed with the access token
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        private bool StopPreLoginRequests(HttpApplication app)
        {
            if (
                    !string.IsNullOrEmpty(app.Request["waitForLogin"]) &&
                    app.Session[Constants.LoginKey] as Boolean? != true
                )
            {
                var count = 0;
                int.TryParse(app.Request["count"], out count);

                if (count > 5)
                    return false;

                System.Threading.Thread.Sleep(600);
                app.Response.Redirect(app.Request.Path + "?waitForLogin=1&count=" + (count + 1));
                return true;
            }

            return false;
        }

        private void AuthorizeGoogle(HttpApplication Application, string urlPath)
        {
            Log.Write("AuthorizeGoogle");
            if (String.IsNullOrEmpty(BLL.Users.GetGoogleID()))
                EndResponse(Application.Response, new GadgetResponse<Object> { ErrorMessage = "Fail - missing id", Status = GadgetResponseStatus.ID_Missing });
            else if (!BLL.Users.ValidateGoogleSig())
                EndResponse(Application.Response, new GadgetResponse<Object> { ErrorMessage = "Fail - unvalid signature", Status = GadgetResponseStatus.Signitue_Error });
            else
            {
                Log.Write("AuthorizeGoogle2");

                var user = Users.Get().Where(Users.Columns.GoogleID, Actions.Equal, BLL.Users.GetGoogleID())
                                            .Where(Users.Columns.Token, Actions.NotEqual, null).SelectOne();
                if (user == null)
                {// user does not exist so he has to perform oauth
                    Log.Write("user == null");
                    try
                    {
                        var authorizeLink = BLL.Users.GetAuthorizationLink();
                        EndResponse(Application.Response, new GadgetResponse<Object> { RedirectUrl = authorizeLink, ErrorMessage = BLL.Users.GetGoogleID()  + " was not found"});
                    }
                    catch(Exception e)
                    {
                    }
                }
                else
                {// user exists
                    Users.EnsureSession(user);// set session for further work in this request
                    if (urlPath.EndsWith("/login/keepAlive"))// this call is performed in order to refresh the session
                        RefreshSession(Application);
                }
            }
        }

        public void EndResponse(HttpResponse res, Object obj)
        {
            JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();

            res.Clear();
            res.Write(jsonSerializer.Serialize(obj));
            HttpContext.Current.ApplicationInstance.CompleteRequest();
        }
    }

    public class UrlRewriteModule : IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.PreRequestHandlerExecute += new EventHandler(context_BeginRequest);
        }

        public void Dispose()
        {

        }

        
        private readonly Regex r = new Regex("^/gmail(/canvas)?/((content|scripts|images|tinymce).*)$", RegexOptions.IgnoreCase);
        void context_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication Application = sender as HttpApplication;

            var match = r.Match(Application.Request.Url.AbsolutePath);
            if (match.Success)
            {
                var fileName = match.Groups[2].Value;
                Application.Context.RewritePath("/" + fileName);
            }
        }
    }

}

